/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
#include <iostream>
#include <fstream>
#include "itkByteSwapper.h"
#include "itkNiftiImageIO.h"
#include "itkImageFileReader.h"
#include "itkImageRegionConstIterator.h"
#include "itkMath.h"
#include "itkAnatomicalOrientation.h"

// debug
#include <map>

namespace
{
//
// Analyze 7.5 header -- this describes the data below,
// as an 6 x 6 x 8 image of float pixels
const unsigned char LittleEndian_hdr[] = {
  0x5c, 0x01, 0x00, 0x00, 0x46, 0x4c, 0x4f, 0x41, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00,
  0x72, 0x00, 0x04, 0x00, 0x06, 0x00, 0x06, 0x00, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80,
  0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
//
// float data, represented as a char stream, in little-endian
// order
const unsigned char LittleEndian_img[] = {
  0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80,
  0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00,
  0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00,
  0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x50, 0x43,
  0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0,
  0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00,
  0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00,
  0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43,
  0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10,
  0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00,
  0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00,
  0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43,
  0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50,
  0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00,
  0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00,
  0x00, 0xa0, 0x42, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41,
  0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10,
  0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00,
  0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00,
  0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42,
  0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0,
  0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00,
  0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x10, 0x43, 0x00,
  0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41,
  0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80,
  0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00, 0x10, 0x43, 0x00, 0x00,
  0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x80, 0x41, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00,
  0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x50, 0x43,
  0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0xa0,
  0x42, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0x50, 0x43, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00,
  0xa0, 0x42, 0x00, 0x00, 0xa0, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00,
  0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43,
  0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30,
  0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00,
  0x40, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00,
  0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43,
  0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70,
  0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00,
  0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00,
  0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42,
  0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30,
  0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00,
  0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00,
  0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42,
  0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0,
  0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00,
  0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00,
  0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42,
  0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40,
  0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00,
  0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00,
  0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43,
  0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0,
  0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00,
  0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43, 0x00,
  0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x30, 0x43,
  0x00, 0x00, 0x30, 0x43, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x40, 0x42, 0x00, 0x00, 0x70,
  0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00,
  0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0xe0, 0x42, 0x00,
  0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43, 0x00, 0x00, 0x70, 0x43,
  0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42, 0x00, 0x00, 0xe0, 0x42,
};

/** WriteFile
 * Write out a char array as binary
 */
int
WriteFile(const std::string & name, const unsigned char * buf, size_t buflen)
{
  std::ofstream f(name.c_str(), std::ios::binary | std::ios::out);
  if (!f.is_open())
  {
    return EXIT_FAILURE;
  }
  f.write(reinterpret_cast<const char *>(buf), buflen);
  f.close();
  return EXIT_SUCCESS;
}

/** ReadFile
 * read an image from disk
 */
template <typename TImage>
typename TImage::Pointer
ReadImage(const std::string &                     fileName,
          itk::NiftiImageIOEnums::Analyze75Flavor analyze_mode = itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM)
{
  using ReaderType = itk::ImageFileReader<TImage>;

  auto                                      reader = ReaderType::New();
  const typename itk::NiftiImageIO::Pointer imageIO = itk::NiftiImageIO::New();
  {
    imageIO->SetLegacyAnalyze75Mode(analyze_mode);
    reader->SetImageIO(imageIO);
    reader->SetFileName(fileName.c_str());
    try
    {
      reader->Update();
    }
    catch (const itk::ExceptionObject & err)
    {
      std::cout << "Caught an exception: " << std::endl;
      std::cout << err << ' ' << __FILE__ << ' ' << __LINE__ << std::endl;
      throw;
    }
    catch (...)
    {
      std::cout << "Error while reading in image  " << fileName << std::endl;
      throw;
    }
  }
  typename TImage::Pointer image = reader->GetOutput();
  return image;
}

} // namespace


int
itkNiftiAnalyzeContentsAndCoordinatesTest(char *                                  argv[],
                                          unsigned char                           hist_orient_code,
                                          itk::AnatomicalOrientation              expected_code,
                                          itk::NiftiImageIOEnums::Analyze75Flavor analyze_mode,
                                          bool                                    flip_x = false)
{
  std::string hdrName(argv[1]);
  hdrName += "/littleEndian_";
  hdrName += expected_code.GetAsPositiveStringEncoding();
  hdrName += ".hdr";
  std::string imgName(argv[1]);
  imgName += "/littleEndian_";
  imgName += expected_code.GetAsPositiveStringEncoding();
  imgName += ".img";

  // hack the header to have proper orientation code
  unsigned char tweaked_hdr[sizeof(LittleEndian_hdr)];
  memcpy(tweaked_hdr, LittleEndian_hdr, sizeof(LittleEndian_hdr));
  tweaked_hdr[252] = hist_orient_code;

  // hack the header to flip X step
  if (flip_x)
  {
    float negative_x = -1.f;
    memcpy(tweaked_hdr + 80, &negative_x, sizeof(float));
  }

  if (WriteFile(hdrName, tweaked_hdr, sizeof(LittleEndian_hdr)) != EXIT_SUCCESS)
  {
    std::cerr << "itkNiftiAnalyzeContentsAndCoordinatesTest: failed to write " << hdrName << std::endl;
    return EXIT_FAILURE;
  }
  if (WriteFile(imgName, LittleEndian_img, sizeof(LittleEndian_img)) != EXIT_SUCCESS)
  {
    std::cerr << "itkNiftiAnalyzeContentsAndCoordinatesTest: failed to write " << imgName << std::endl;
    return EXIT_FAILURE;
  }
  //
  // read the image just written back in.
  using ImageType = itk::Image<float, 3>;
  ImageType::Pointer img;
  try
  {
    img = ReadImage<ImageType>(hdrName, analyze_mode);
  }
  catch (...)
  {
    if (analyze_mode == itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeReject)
    {
      std::cerr << "Failure is expected" << std::endl << std::endl;
    }
    return EXIT_FAILURE;
  }

  const auto *                             fPtr = reinterpret_cast<const float *>(LittleEndian_img);
  itk::ImageRegionConstIterator<ImageType> it(img, img->GetLargestPossibleRegion());
  it.GoToBegin();
  for (; !it.IsAtEnd(); ++it, ++fPtr)
  {
    //
    // in the unlikely event we're testing on a big-endian machine, do
    // byte swapping on floats pulled from the little-endian array.
    float cur = *fPtr;
    itk::ByteSwapper<float>::SwapFromSystemToLittleEndian(&cur);
    if (itk::Math::NotExactlyEquals(it.Get(), cur))
    {
      std::cerr << "itkNiftiAnalyzeContentsAndCoordinatesTest: expected pixel value " << cur << " but found "
                << it.Get() << std::endl;
      return EXIT_FAILURE;
    }
  }

  const itk::AnatomicalOrientation orientation_code(img->GetDirection());
  // verify the correct orientation :
  if (orientation_code != expected_code)
  {
    std::cerr << "Analyze orientation " << static_cast<int>(hist_orient_code) << std::endl;
    std::cerr << "expected orientation " << expected_code.GetAsPositiveStringEncoding() << " but found "
              << orientation_code.GetAsPositiveStringEncoding() << std::endl;
    return EXIT_FAILURE;
  }

  // TODO: check origin and spacing too
  std::cout << "Analyze orientation :" << static_cast<int>(hist_orient_code) << std::endl
            << "Analyze flavor :" << analyze_mode << std::endl
            << "negative x step:" << (flip_x ? "true" : "false") << std::endl
            << "Origin   :" << img->GetOrigin() << std::endl
            << "Spacing  :" << img->GetSpacing() << std::endl
            << "Code     :" << orientation_code.GetAsPositiveStringEncoding() << std::endl
            << "Direction:" << img->GetDirection() << std::endl;

  return EXIT_SUCCESS;
}

int
itkNiftiReadAnalyzeTest(int argc, char * argv[])
{
  if (argc < 2)
  {
    std::cerr << "itkNiftiReadAnalyzeTest: Missing test directory argument" << std::endl;
    return EXIT_FAILURE;
  }

  // check that when we reject Analyze, it can't be read


  // NOTE: according to the information from
  // https://web.archive.org/web/20121116093304/http://wideman-one.com/gw/brain/analyze/formatdoc.htm Analyze code 5
  // should have been PSR but it was revised in NIFTI somehow to PIL

  return itkNiftiAnalyzeContentsAndCoordinatesTest(
           argv,
           0,
           itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                      itk::AnatomicalOrientation::CoordinateEnum::PosteriorToAnterior,
                                      itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
           itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeReject) != EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               0,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::PosteriorToAnterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               1,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior,
                                          itk::AnatomicalOrientation::CoordinateEnum::PosteriorToAnterior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               2,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::PosteriorToAnterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior,
                                          itk::AnatomicalOrientation::CoordinateEnum::RightToLeft),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               3,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::AnteriorToPosterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               4,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::SuperiorToInferior,
                                          itk::AnatomicalOrientation::CoordinateEnum::PosteriorToAnterior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               5,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::PosteriorToAnterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior,
                                          itk::AnatomicalOrientation::CoordinateEnum::LeftToRight),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM) == EXIT_FAILURE ||
             // ITK4 default behaviour: reader should ignore orientation code and always produce RAI ,
             // there should be a warning on console
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               0,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::AnteriorToPosterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4Warning) == EXIT_FAILURE ||
             // ITK4 reader should ignore orientation code and always produce RAI
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               0,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::AnteriorToPosterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               1,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::AnteriorToPosterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               2,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::AnteriorToPosterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               3,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::AnteriorToPosterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               5,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::AnteriorToPosterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE ||
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               5,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::AnteriorToPosterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4) == EXIT_FAILURE ||
             // flip X  axis , SPM reader should respect this
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               0,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::LeftToRight,
                                          itk::AnatomicalOrientation::CoordinateEnum::PosteriorToAnterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeSPM,
               true) == EXIT_FAILURE ||
             // flip X  axis , ITK4 reader should respect this
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               0,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::LeftToRight,
                                          itk::AnatomicalOrientation::CoordinateEnum::AnteriorToPosterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeITK4,
               true) == EXIT_FAILURE ||
             // flip X  axis , FSL reader should ignore this
             itkNiftiAnalyzeContentsAndCoordinatesTest(
               argv,
               0,
               itk::AnatomicalOrientation(itk::AnatomicalOrientation::CoordinateEnum::RightToLeft,
                                          itk::AnatomicalOrientation::CoordinateEnum::PosteriorToAnterior,
                                          itk::AnatomicalOrientation::CoordinateEnum::InferiorToSuperior),
               itk::NiftiImageIOEnums::Analyze75Flavor::AnalyzeFSL,
               true) == EXIT_FAILURE

           ? EXIT_FAILURE
           : EXIT_SUCCESS;
}
